﻿using ServerListerner.Connections;
using ServerListerner.Enums;
using ServerListerner.Handlers;
using ServerListerner.Helper;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using static System.Collections.Specialized.BitVector32;

namespace ServerListerner
{
    public class HTTPServer : IDisposable
    {
        private readonly int _port;
        private readonly IPAddress _ipaddr;
        private readonly Socket _socket;
        private Action<IHTTPConnection>? _action;
        public bool IsRunning = false;
        public List<ListenPath> _path = new List<ListenPath>();
        private IRouterHandler _handler;

        public HTTPServer(int port)
        {
            this._port = port;
            this._handler = new RouterHandler();
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        }
        public HTTPServer(IPAddress ipaddr, int port) : this(port)
        {
            this._ipaddr = ipaddr;
        }
        public HTTPServer(string ip, int port) : this(IPAddress.Parse(ip), port) { }
        public bool StartListen(Action<IHTTPConnection>? action = null)
        {
            try
            {
                if (_port.PortInUse())
                    return false;
                IsRunning = true;
                _action = action;
                IPAddress iPAddress = _ipaddr == null || _ipaddr == IPAddress.None ? IPAddress.Any : _ipaddr;
                _socket.Bind(new IPEndPoint(iPAddress, this._port));
                _socket.Listen(int.MaxValue);
                if (!_port.PortInUse())
                    return false;
                StartTCPAccept();
                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }
        public void SetPath(MethodEnum method,string? path=null)
        {
            string? Method = method.GetDescription()?.ToLower()??"post";
            path = path ?? "/";
            if(!this._path.Any(s=>s.Method== Method&&s.Path==path))
                lock (_path)
                {
                    this._path.Add(new ListenPath()
                    {
                        Method = Method,
                        Path = path
                    });
                }
        }
        public void RemovePath(MethodEnum method, string? path = null)
        {
            string? Method = method.GetDescription()?.ToLower() ?? "post";
            path = path ?? "/";
            lock (_path)
            {
                this._path.RemoveAll(s => s.Method == Method && s.Path == path);
            }
        }
        public bool StopListen()
        {
            IsRunning = false;
            this.CloseSocket();
            return !_port.PortInUse();
        }
        private void StartTCPAccept()
          => this._socket.BeginAccept(TCPAccept, this._socket);
        private void TCPAccept(IAsyncResult iar)
        {
            try
            {
                if (!IsRunning) return;
                if (iar.IsCompleted && iar.AsyncState != null)
                {
                    Socket client = _socket.EndAccept(iar);
                    if (client.Connected)
                    {
                        new HTTPConnection(client, _action,this._path,this._handler,Encoding.UTF8);
                    }
                }
            }
            finally
            {
                StartTCPAccept();
            }
        }

        public void Dispose()
        {
            this.CloseSocket();
            _action = null;
        }
        private void CloseSocket()
        {
            this._socket.Shutdown(SocketShutdown.Both);
            this._socket.Close();
            this._socket.Dispose();
        }
    }
}
